package com.gitee.wsl.collections.list

import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow

//package ai.hypergraph.experimental

abstract class BaseLink<T,E: BaseLink<T, E>>(val head: T, var next: E? = null) {

  abstract fun last():E
  abstract fun first():E

  fun lastValue(): T = last().head
  fun firstValue() = first().head

  override fun toString(): String = "[$head]" + "->" + next.toString()
}

fun <T,E: BaseLink<T, E>> BaseLink<T, E>.asFlow(): Flow<T> {
  return flow{
    var e: BaseLink<T, E>? = this@asFlow
    while(e != null){
      emit(e.head)
      e = e.next
    }
  }
}

class SingLink<T>(head: T, next: SingLink<T>?=null): BaseLink<T, SingLink<T>>(head,next){
   override fun last(): SingLink<T> = if (next == null) this else next!!.last()
   override fun first() = this

   operator fun plus(t: T): SingLink<T> = if (next == null) SingLink(head, SingLink(t)) else SingLink(head, next!! + t)
}

class DoublyLink<T>(
  head: T,
  var prev: DoublyLink<T>?=null,
  next: DoublyLink<T>?=null
): BaseLink<T, DoublyLink<T>>(head,next), Iterable<DoublyLink<T>> {
  //val prev: DoublyLink<T> by lazy { prev(this) }
  //override var next: DoublyLink<T>? by lazy { next(this) }
  val size: Int by lazy { 1 + (if (!hasNext()) 0 else this.next!!.size) }
  val tail: T by lazy { if (!hasNext()) head else this.next!!.tail }

  override fun last(): DoublyLink<T> = if ( next == null) this else next!!.last()
  override fun first(): DoublyLink<T> = if( prev != null) prev!!.first() else this

  operator fun plus(t: T): DoublyLink<T> {
    val other = DoublyLink(head = t)
    return plus(other)
  }

  operator fun plus(other: DoublyLink<T>): DoublyLink<T> {
    val oldNext = next
    if(oldNext != null){
      val last = other.last()
      last.next = oldNext
      oldNext.prev=last
    }
    other.prev = this
    next = other
    return this
  }


// TODO: can these two be merged?
  //operator fun plus(other: DoublyLink<T>): DoublyLink<T> = other.fold(this) { a, b -> a + b.head }
  /* private fun append(next: DoublyLink<T>): DoublyLink<T> =
   DoublyLink(
      // n.b. head is advanced! (wrong view for publicly-facing API)
      head = next.head,
      prev = this ,
      next = if (!next.hasNext()) next
        else next.append(next.next)
    )*/

// TODO: This would appear to work, but head/prev is broken
//  operator fun plus(next: DoublyLink<T>): DoublyLink<T> =
//    DoublyLink(
//      head = head,
//      prev = { this },
//      next = { me ->
//        if (!next.hasNext()) me
//        else me + next.next
//      }
//    )

  fun insert(t: T): DoublyLink<T> = (this + t).next!!

  /*fun insert(t: T): DoublyLink<T> =
    DoublyLink(
      head = head,
      next = { me ->
        if (!hasNext()) this + t
        else DoublyLink(t, { me }, { it.append(next) })
//        else DoublyLink(t, { me }, { it + (next) })
      }
    )*/

  operator fun get(i: Int): DoublyLink<T> = if (i == 0) this else next!![i - 1]

  fun reversed(): DoublyLink<T> = if (!hasNext()) this else next!!.reversed() + head

  override fun toString(): String =
    if (!hasNext()) "[$head]" else "[$head]<->$next"

  fun hasNext() = next != null
  fun hasPrev() = prev != null

  override fun iterator() = generateSequence(this) { if (it.next != null) it.next else null }.iterator()

   fun prevIterator() = generateSequence(this) { if (it.prev != null) it.prev else null }.iterator()
}